/*
 
 TopMind jLynx
 Copyright (c) 2004-onwards. TopMind Systems LLC.
 All rights reserved.
 
 This file is part of TopMind jLynx.
 
 TopMind jLynx is free software; you can redistribute it and/or modify
 it under the terms of the License. See website for License.
 
 */
package org.rishi.framework.db;

import java.beans.Introspector;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author MHT E-mail:mht1984@126.com
 * @version 创建时间：2010-8-31 下午05:00:37
 *
 */
public class BeanUtils {

    private static Map cache = new HashMap();

    private static Logger logger = LoggerFactory.getLogger(BeanUtils.class);

    private static Date convertDate(String dtStr) {

        logger.trace("Setting date using pattern: " + ConfigParser.datePattern);
        SimpleDateFormat format = new SimpleDateFormat(ConfigParser.datePattern);
        try {
            return new Date(format.parse(dtStr).getTime());
        } catch (ParseException e) {
            logger.error("Trouble setting Date! " + dtStr);
            return null;
        }
    }

    private static String convertDate(java.sql.Date dt) {
        SimpleDateFormat format = new SimpleDateFormat(ConfigParser.datePattern);
        return format.format(dt);
        // return "";
    }

    private static Timestamp convertTimestamp(String dtStr) {

        String pattern = (dtStr.length() > 10) ? ConfigParser.tsPattern
                : ConfigParser.datePattern;
        logger.trace("Setting timestamp using pattern: " + pattern);
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            long time = format.parse(dtStr).getTime();
            return new Timestamp(time);
        } catch (ParseException e) {
            logger.error("Touble setting Timestamp! " + dtStr);
            return null;
        }
    }

    static String decapitalize(String fieldName) {
        return Introspector.decapitalize(fieldName);
    }

    /**
     * This method performs in a case-sensitive manner
     *
     * @param target
     * @return Map
     */
    static Map describe(Object target) {

        Map retMap = new HashMap();
        List retList = new LinkedList();
        Method[] methods = (Method[]) cache.get(target.getClass());
        if (methods == null) {
            methods = target.getClass().getMethods();
            cache.put(target.getClass(), methods);
        }

        for (int i = 0; i < methods.length; i++) {

            String method = methods[i].getName();

            // added v1.5.0 to remove arrays and Lists from POJOs
            if (methods[i].getReturnType().isArray()
                    || methods[i].getReturnType().equals(List.class)
                    || (methods[i].getParameterTypes().length == 1 && methods[i]
                    .getParameterTypes()[0].equals(List.class))) {
                logger.trace("Removing a List or Array field! " + method);
                continue;
            }

            if (method.indexOf("set") == 0 || method.indexOf("get") == 0) {
                retList.add(method.substring(3, method.length()));
            } else if (method.indexOf("is") == 0) {// added this in v1.2.1
                retList.add(method.substring(2, method.length()));
            }

        }

        Collections.sort(retList);
        Object[] props = retList.toArray();
        retList.clear();
        for (int i = 0; i < props.length - 1; i++) {

            if (props[i].equals(props[i + 1])) {
                String prop = decapitalize(props[i].toString());
                retMap.put(prop, getValue(prop, target));
                // Added in v1.6
                String test = target.getClass().getName() + "." + prop;
                if (ConfigParser.COL_MAPPING.containsKey(test)) {
                    String col = (String) ConfigParser.COL_MAPPING.get(test);
                    retMap.remove(prop);
                    retMap.put(col, getValue(prop, target));
                }
            }

        }
        retList = null;
        return retMap;

    }

    public static Class getType(String property, Object target) {

        Class ret = Object.class;
        property = "set" + property;

        Method[] methods = (Method[]) cache.get(target.getClass());
        if (methods == null) {
            methods = target.getClass().getMethods();
            cache.put(target.getClass(), methods);
        }

        for (int i = 0; i < methods.length; i++) {

            if (property.equalsIgnoreCase(methods[i].getName())) {

                Class[] paramClass = methods[i].getParameterTypes();
                if (paramClass.length == 1) {
                    return paramClass[0];
                }

            }

        }

        return ret;

    }

    /**
     * This method performs in a case-insensitive manner
     */
    static Object getValue(String property, Object target) {

        logger.trace("Seeking property=" + property + " from " + target);

        String get = "get" + property;
        String is = "is" + property;

        Method[] methods = (Method[]) cache.get(target.getClass());
        if (methods == null) {
            methods = target.getClass().getMethods();
            cache.put(target.getClass(), methods);
        }

        for (int i = 0; i < methods.length; i++) {

            if (get.equalsIgnoreCase(methods[i].getName())
                    || is.equalsIgnoreCase(methods[i].getName())) {

                logger.trace("methodName=" + methods[i].getName());

                try {
                    return methods[i].invoke(target, (Object[]) null);
                } catch (IllegalArgumentException ex) {
                    // ex.printStackTrace();
                } catch (IllegalAccessException ex) {
                    // ex.printStackTrace();
                } catch (InvocationTargetException ex) {
                    // ex.printStackTrace();
                }

            }
        }
        return null;

    }

    /**
     * This method performs in a case-insensitive manner
     */
    public static void setValue(String property, Object target, Object value) {

        String test = property.toLowerCase() + "."
                + target.getClass().getName();
        logger.trace(test);
        if (ConfigParser.COL_MAPPING.containsKey(test)) {
            // logger.debug("\n\n\nCheck for correct column/property!");
            property = (String) ConfigParser.COL_MAPPING.get(test);
            logger.trace("Using property :: " + property);
        }

        property = "set" + property;

        logger.trace("setting value=" + value);

        Method[] methods = (Method[]) cache.get(target.getClass());
        if (methods == null) {
            methods = target.getClass().getMethods();
            cache.put(target.getClass(), methods);
        }

        for (int i = 0; i < methods.length; i++) {

            if (property.equalsIgnoreCase(methods[i].getName())) {

                Class[] paramClass = methods[i].getParameterTypes();
                if (paramClass.length == 1) {

                    Class cls = paramClass[0];

                    logger.trace("type=" + cls);
                    try {

                        methods[i].invoke(target, new Object[]{value});

                    } catch (Exception ex) {

                        // handle Integer and int arrays
                        if (value != null && value.getClass().isArray()) {
                            try {
                                String clsName = cls.getName();
                                String[] va = (String[]) value;
                                if (value instanceof String[]
                                        && clsName.equals("[I")) {

                                    int[] int2 = new int[va.length];
                                    for (int i2 = 0; i2 < va.length; i2++) {
                                        int2[i2] = Integer.parseInt(va[i2]);
                                    }

                                    methods[i].invoke(target,
                                            new Object[]{int2});

                                } else if (value instanceof String[]
                                        && clsName
                                        .equals("[Ljava.lang.Integer;")) {

                                    Integer[] int2 = new Integer[va.length];
                                    for (int i2 = 0; i2 < va.length; i2++) {
                                        int2[i2] = new Integer(va[i2]);
                                    }

                                    methods[i].invoke(target,
                                            new Object[]{int2});

                                }

                                return;

                            } catch (Exception e3) {
                                logger.warn("Could not set array");
                                return;
                            }
                        }

                        Constructor ctor;
                        try {

                            ctor = cls.getConstructor(new Class[]{value.getClass()});

                            logger.trace("ctor = " + ctor);

                            Object newVal = ctor.newInstance(new Object[]{value});

                            methods[i].invoke(target, new Object[]{newVal});

                        } catch (Exception e2) {

                            if (cls.getName().equals("java.sql.Timestamp")) {
                                if (value instanceof String) {
                                    try {
                                        methods[i]
                                                .invoke(
                                                        target,
                                                        new Object[]{convertTimestamp(value
                                                                .toString())});

                                    } catch (Exception e3) {
                                        e3.printStackTrace();
                                    }
                                }
                            } else if (cls.getName().equals("java.sql.Date")) {
                                if (value instanceof String) {
                                    try {
                                        methods[i]
                                                .invoke(
                                                        target,
                                                        new Object[]{convertDate(value
                                                                .toString())});

                                    } catch (Exception e3) {
                                        e3.printStackTrace();
                                    }
                                }
                            }
                        }
                    }

                }

            }
        }

    }

    static String toJSFormBind(String form, Object bean) {
        StringBuffer result = new StringBuffer("if( document." + form + " ){\n");
        Map pairs;
        if (bean instanceof Map)
            pairs = (Map) bean;
        else
            pairs = describe(bean);

        Iterator iter = pairs.keySet().iterator();
        while (iter.hasNext()) {

            Object obj = iter.next();
            Object value = pairs.get(obj);
            if (value != null) {
                result.append("  try { document.").append(form).append(".").append(obj.toString()).append(".value = '").append(value.toString()).append("'; } catch(e){}\n");
            }

        }
        result.append("}");

        return result.toString();
    }

    static String toJSON(Object bean) {

        if (bean == null)
            return null;

        StringBuffer result = new StringBuffer("{\n");

        Map pairs;
        if (bean instanceof Map)
            pairs = (Map) bean;
        else
            pairs = describe(bean);

        Iterator iter = pairs.keySet().iterator();
        while (iter.hasNext()) {

            Object obj = iter.next();

            result.append("\t\"").append(obj).append("\": ");
            Object value = pairs.get(obj);
            if (value != null) {
                // adjusted for v1.6.2
                if (value instanceof String) {
                    value = StringUtils.replace(value.toString(), "\\", "\\\\");
                    value = StringUtils.replace(value.toString(), "\"", "\\\"");
                    value = StringUtils.replace(value.toString(), "\'", "\\\'");
                    value = StringUtils.replace(value.toString(), "\n", "\\n");
                    value = StringUtils.replace(value.toString(), "\r", "\\r");
                    value = StringUtils.replace(value.toString(), "\b", "\\b");
                    value = StringUtils.replace(value.toString(), "\t", "\\t");
                } else if (value instanceof Timestamp) {
                    long time = ((Timestamp) value).getTime();
                    value = "" + time + "";
                } else if (value instanceof Date) {
                    value = convertDate((Date) value);
                }

                result.append("\"").append(value).append("\"");
            } else
                result.append(value);

            if (iter.hasNext())
                result.append(",\n");
            else
                result.append("\n}");

        }

        return result.toString();
    }

    static String toXml(List list, String element) {
        StringBuffer xml = new StringBuffer();
        Iterator iter = list.iterator();
        while (iter.hasNext()) {
            xml.append(toXml(iter.next(), element)).append("\n");
        }
        return xml.toString();
    }

    static String toXml(Object bean, String element) {

        StringBuffer sb = new StringBuffer("");

        if (bean == null)
            return null;
        else {

            String className = bean.getClass().getName();
            if (element == null)
                element = className.substring(className.lastIndexOf(".") + 1).toLowerCase();
            sb.append("<").append(element).append(" class=\"").append(className).append("\">\n");

            if (!(bean instanceof Map)) {
                Map props = BeanUtils.describe(bean);
                Iterator keys = props.keySet().iterator();
                while (keys.hasNext()) {
                    String key = keys.next().toString();
                    Object value = props.get(key);
                    if (value == null)
                        value = "";
                    sb.append(" <").append(key).append(">").append(value).append("</").append(key).append(">\n");
                }
            } else {
                Iterator keys = ((Map) bean).keySet().iterator();
                while (keys.hasNext()) {
                    String key = keys.next().toString();
                    Object value = ((Map) bean).get(key);
                    if (value == null)
                        value = "";
                    sb.append(" <").append(key).append(">").append(value).append("</").append(key).append(">\n");
                }
            }

            sb.append("</").append(element).append(">");

        }
        return sb.toString();
    }

    private BeanUtils() {
    }

}
